package org.springblade.modules.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateException;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lark.oapi.Client;
import com.lark.oapi.service.im.v1.model.CreateMessageReq;
import com.lark.oapi.service.im.v1.model.CreateMessageReqBody;
import com.lark.oapi.service.im.v1.model.CreateMessageResp;
import lombok.AllArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.springblade.common.config.FeiShuConfiguration;
import org.springblade.common.enums.FeishuClientEnum;
import org.springblade.common.utils.CommonUtil;
import org.springblade.common.utils.PoiExcelUtil;
import org.springblade.common.utils.feishu.FeiShuUtil;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.modules.admin.dto.UserCertDTO;
import org.springblade.modules.admin.entity.UserCertRecord;
import org.springblade.modules.admin.enums.CertStatusEnum;
import org.springblade.modules.admin.service.ICertClassificationService;
import org.springblade.modules.admin.service.IUserCertRecordService;
import org.springblade.modules.admin.vo.UserCertRecordVO;
import org.springblade.modules.admin.vo.UserCertVO;
import org.springblade.modules.admin.vo.cert.CertClassificationWorkVO;
import org.springblade.modules.admin.vo.cert.ExcelEntityVO;
import org.springblade.modules.admin.vo.cert.UserCertStatisticsVO;
import org.springblade.modules.system.entity.Dict;
import org.springblade.modules.system.entity.User;
import org.springblade.modules.system.service.IDictService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import org.springblade.modules.admin.mapper.UserCertMapper;
import org.springblade.modules.admin.entity.UserCert;
import org.springblade.modules.admin.service.UserCertService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

@AllArgsConstructor
@Service
public class UserCertServiceImpl extends BaseServiceImpl<UserCertMapper, UserCert> implements UserCertService{

	private final IUserCertRecordService userCertRecordService;
	private final ICertClassificationService certClassificationService;
	private final BladeRedis bladeRedis;
	private final FeiShuUtil feiShuUtil;
	private final IDictService dictService;
	private static final String CERT_EXCEL_ERR_CACHE = "cert:info:excel:err:";
	private final String[] CERT_HEAD_TITLE = {"手机号码", "证件类型", "作业项目", "证书编号", "有效期开始", "有效期结束", "失败提示"};


	private final FeiShuConfiguration feiShuConfiguration;
	@Override
	public IPage<UserCertVO> selectUserCertPage(IPage<UserCertVO> page, UserCertDTO userCert) {
		return page.setRecords(baseMapper.selectUserCertPage(page, userCert));
	}

	@Override
	public boolean insert(UserCert userCert) {
		if (!checkCertUniqueness(userCert)) {
			throw new ServiceException("当前人员已存在当前证件");
		}
		return save(userCert);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean update(UserCert userCert) {
		if (Func.isEmpty(userCert.getId())) {
			throw new ServiceException("数据异常，请稍后再试!");
		}
		if (!checkCertUniqueness(userCert)) {
			throw new ServiceException("当前人员已存在当前证件");
		}

		UserCert oldUserCert = getById(userCert.getId());
		UserCertRecord userCertRecord = new UserCertRecord();
		BeanUtil.copyProperties(oldUserCert, userCertRecord, "id");
		userCertRecord.setUserCertId(userCert.getId());

		return updateById(userCert) && userCertRecordService.save(userCertRecord);
	}

	@Override
	public UserCertVO detail(Long id) {
		UserCertVO userCert = baseMapper.detail(id);
		if (Func.isNotEmpty(userCert)) {
			List<UserCertRecordVO> certRecordList = userCertRecordService.selectListByUserCertId(id);
			userCert.setUserCertRecordList(certRecordList);
		}
		return userCert;
	}

	@Override
	public List<UserCertVO> selectListByUserId(Long userId) {
		return baseMapper.selectListByUserId(userId);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean batchUpdate(List<UserCert> userCerts) {
		if (Func.isNull(userCerts)) {
			return false;
		}

		for (UserCert userCert : userCerts) {
			if (userCert.getId() == null || userCert.getStartTime() == null
				|| userCert.getEndTime() == null || Func.isBlank(userCert.getCertImg())) {
				throw new ServiceException("非法参数!");
			}
			if (!checkCertUniqueness(userCert)) {
				throw new ServiceException("当前人员已存在当前证件");
			}

			UserCert updateCert = new UserCert();
			updateCert.setId(userCert.getId());
			updateCert.setCertImg(userCert.getCertImg());
			updateCert.setStartTime(userCert.getStartTime());
			updateCert.setEndTime(userCert.getEndTime());
			this.update(updateCert);
		}

		return true;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void batchSaveOrUpdate(Long userId, List<UserCert> userCerts) {
		if (Func.isEmpty(userCerts)) {
			this.remove(Wrappers.<UserCert>lambdaQuery().eq(UserCert :: getUserId, userId));
		} else {
			long checkRepeatData =userCerts.stream().map(t -> String.format("%s-%s", t.getCertType(), t.getWorkPro())).distinct().count();
			if (userCerts.size() > checkRepeatData) {
				throw new ServiceException("人员证件信息存在重复!");
			}
			List<Long> ids = userCerts.stream().map(UserCert :: getId).collect(Collectors.toList());
			this.remove(Wrappers.<UserCert>lambdaQuery().eq(UserCert :: getUserId, userId).notIn(UserCert :: getId, ids));
			for (UserCert userCert : userCerts) {
				userCert.setUserId(userId);
				if (Func.isEmpty(userCert.getId())) {
					this.insert(userCert);
				} else {
					this.update(userCert);
				}
			}
		}
	}

	@Override
	public UserCertStatisticsVO statistics() {
		UserCertStatisticsVO statistics = new UserCertStatisticsVO();
		List<UserCert> list = this.list();
		Map<Integer, Long> statisticsMap = list.stream().collect(Collectors.groupingBy(UserCert :: getStatus, Collectors.counting()));
		statistics.setNormalCount(statisticsMap.get(CertStatusEnum.NORMAL.getId()));
		statistics.setExpiringSoonCount(statisticsMap.get(CertStatusEnum.EXPIRING_SOON.getId()));
		statistics.setExpiredCount(statisticsMap.get(CertStatusEnum.EXPIRED.getId()));
		statistics.setTotal((long) list.size());
		return statistics;
	}

	@Override
	public boolean checkCertUniqueness(UserCert userCert) {
		LambdaQueryWrapper<UserCert> queryWrapper = Wrappers.lambdaQuery();
		if (Func.isNotEmpty(userCert.getId())) {
			queryWrapper.ne(UserCert :: getId, userCert.getId());
		}

		queryWrapper.eq(UserCert :: getUserId, userCert.getUserId())
			.eq(UserCert :: getCertType, userCert.getCertType())
			.eq(UserCert :: getWorkPro, userCert.getWorkPro());
		int uniqueness = this.count(queryWrapper);

		return uniqueness <= 0;
	}

	@Override
	public void deleteByUserId(Long userId) {
		this.remove(Wrappers.<UserCert>lambdaQuery().eq(UserCert :: getUserId, userId));
	}

	@Override
	public void downExcelTemplate(HttpServletResponse response) {
		XSSFWorkbook workbook = null;
		//读取源文件
		ClassPathResource resource = new ClassPathResource("templates/证件.xlsx");

        try {
            workbook = new XSSFWorkbook(resource.getInputStream());
        } catch (IOException e) {
			throw new ServiceException("证件模板文件异常！");
        }

		String fileName = "证件模板_" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + ".xlsx";
		CommonUtil.exportStream(workbook, response, fileName);
    }

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Map<String, Object> uploadExcel(MultipartFile file, boolean isCover) {
		if (file.isEmpty()) {
			throw new ServiceException("上传文件不能为空");
		}

		Workbook wb = null;
		try {
			wb = WorkbookFactory.create(file.getInputStream());
		} catch (Exception e) {
			throw new ServiceException("请使用正确的模板导入数据");
		}
		List<Map<String, Object>> readAll = PoiExcelUtil.readExcel(wb, 0, 1, 2, 0);
		if (readAll.isEmpty()) {
			throw new ServiceException("导入数据为空");
		}


		// 存储导入数据
		Set<ExcelEntityVO> excelDataList = new LinkedHashSet<>();
		// 存储错误导出数据
		LinkedList<Map<String, Object>> errExportList = new LinkedList<>();
		for (int i = 0; i < readAll.size(); i++) {
			ExcelEntityVO excelEntity = new ExcelEntityVO();
			Map<String, Object> itemMap = readAll.get(i);

			// 手机号码
			String phone = String.valueOf(itemMap.get(CERT_HEAD_TITLE[0]));
			if (Func.isBlank(phone)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","手机号码不能为空；");
				errExportList.add(itemMap);
				continue;
			}
			User user = baseMapper.getUserByPhone(phone, AuthUtil.getTenantId());
			if (Func.isEmpty(user)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","通讯录没有当前人员手机号码；");
				errExportList.add(itemMap);
				continue;
			}
			excelEntity.setPhone(phone);
			excelEntity.setUserId(user.getId());

			// 证件类型
			String certTypeName = String.valueOf(itemMap.get(CERT_HEAD_TITLE[1]));
			if (Func.isBlank(certTypeName)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","证件类型不能为空；");
				errExportList.add(itemMap);
				continue;
			}
			// 作业项目
			String certWorkName = String.valueOf(itemMap.get(CERT_HEAD_TITLE[2]));
			if (Func.isBlank(certWorkName)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","作业项目不能为空；");
				errExportList.add(itemMap);
				continue;
			}
			List<CertClassificationWorkVO> certTypes = certClassificationService.selectCertTypeAndWorks(certTypeName, certWorkName);
			if (Func.isEmpty(certTypes)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","没有当前输入的证件类型");
				errExportList.add(itemMap);
				continue;
			}
			CertClassificationWorkVO certClassificationWork = certTypes.get(0);
			if (Func.isBlank(certClassificationWork.getContent())) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","当前证件类型下，没有输入的作业项目");
				errExportList.add(itemMap);
				continue;
			}
			excelEntity.setCertTypeId(certClassificationWork.getCertTypeId());
			excelEntity.setCertTypeName(certTypeName);
			excelEntity.setCertWorkId(certClassificationWork.getCertWorkId());
			excelEntity.setCertWorkName(certWorkName);

			// 证书编号
			String certNum = String.valueOf(itemMap.get(CERT_HEAD_TITLE[3]));
			if (Func.isBlank(certNum)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","证书编号不能为空；");
				errExportList.add(itemMap);
				continue;
			}
			excelEntity.setCertNum(certNum);

			// 有效期开始
			String startTime = String.valueOf(itemMap.get(CERT_HEAD_TITLE[4]));
			if (Func.isBlank(startTime)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","有效期开始不能为空；");
				errExportList.add(itemMap);
				continue;
			}
			Date startDate = null;
			try {
				startDate = DateUtil.parse(startTime, DatePattern.NORM_DATE_PATTERN);
			} catch (DateException e) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","请输入正确的有效期开始，格式为（yyyy-MM-dd）；");
				errExportList.add(itemMap);
				continue;
			}

			// 有效期结束
			String endTime = String.valueOf(itemMap.get(CERT_HEAD_TITLE[5]));
			if (Func.isBlank(endTime)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","有效期结束不能为空；");
				errExportList.add(itemMap);
				continue;
			}
			Date endDate = null;
			try {
				endDate = DateUtil.parse(endTime, DatePattern.NORM_DATE_PATTERN);
			} catch (DateException e) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","请输入正确的有效期结束，格式为（yyyy-MM-dd）；");
				errExportList.add(itemMap);
				continue;
			}

			// 时间校验
			int checkDate = DateUtil.compare(startDate, endDate);
			if (checkDate > 0) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","开始时间不能大于结束时间；");
				errExportList.add(itemMap);
				continue;
			}
			excelEntity.setStartTime(startTime);
			excelEntity.setEndTime(endTime);
			excelEntity.setItemMap(itemMap);
			excelEntity.setNum(i + 1);
			if (excelDataList.contains(excelEntity)) {
				itemMap.put("num", String.format("第%d条数据出错", i + 1));
				itemMap.put("errMsg","excel表中已存在该条数据；");
				errExportList.add(itemMap);
				continue;
			}
			excelDataList.add(excelEntity);
		}


		for (ExcelEntityVO importEntity : excelDataList) {
			UserCert userCert = new UserCert();
			userCert.setUserId(importEntity.getUserId());
			userCert.setCertType(importEntity.getCertTypeId());
			userCert.setWorkPro(importEntity.getCertWorkId());
			userCert.setCertNum(importEntity.getCertNum());
			userCert.setStartTime(DateUtil.parseDate(importEntity.getStartTime()));
			userCert.setEndTime(DateUtil.parseDate(importEntity.getEndTime()));
			if (isCover) {
				deleteByUserId(importEntity.getUserId());
				save(userCert);
			} else {
				if (!this.checkCertUniqueness(userCert)) {
					importEntity.getItemMap().put("num", String.format("第%d条数据出错", importEntity.getNum()));
					importEntity.getItemMap().put("errMsg","当前人员已存在证件列表；");
					errExportList.add(importEntity.getItemMap());
					continue;
				}
				save(userCert);
			}
		}

		// 将出错数据存到redis
		String redisKey = AuthUtil.getTenantId().concat(StringPool.COLON).concat(CERT_EXCEL_ERR_CACHE).concat(String.valueOf(AuthUtil.getUserId()));
		if (!errExportList.isEmpty()) {
			bladeRedis.setEx(redisKey, errExportList, 30000L);
		} else {
			bladeRedis.del(redisKey);
		}

		Map<String, Object> resultInfo = new HashMap<>(8);
		if(errExportList.isEmpty()){
			resultInfo.put("result",Boolean.TRUE);
		}else {
			resultInfo.put("result",Boolean.FALSE);
		}
		resultInfo.put("total",readAll.size());
		resultInfo.put("succeed", readAll.size() - errExportList.size());
		// 错误提示数据排序
		List<Map<String, Object>> errTipSortList = errExportList.stream().sorted(Comparator.comparing(t -> {
			String errDesc = String.valueOf(t.get("num"));
			return Integer.parseInt(errDesc.substring(1, errDesc.length() - 5));
		})).collect(Collectors.toList());
		resultInfo.put("untreated", errTipSortList);
		return resultInfo;
	}

	@Override
	public void downErrExcel(HttpServletResponse response) {
		Object data = bladeRedis.get(AuthUtil.getTenantId().concat(StringPool.COLON).concat(CERT_EXCEL_ERR_CACHE).concat(String.valueOf(AuthUtil.getUserId())));
		if (Func.isEmpty(data)) {
			throw new ServiceException("未有错误数据导出！");
		}
		LinkedList<Map<String, Object>> errList = (LinkedList<Map<String, Object>>)data;

		XSSFWorkbook workbook = null;
		//读取源文件
		ClassPathResource resource = new ClassPathResource("templates/证件_错误.xlsx");
		try {
			workbook = new XSSFWorkbook(resource.getInputStream());
		} catch (IOException e) {
			throw new ServiceException("证件模板文件异常！");
		}

		XSSFSheet sheet = workbook.getSheetAt(0);
		for (int i = 0; i < errList.size(); i++) {
			Map<String, Object> itemMap = errList.get(i);
			XSSFRow row = sheet.createRow(i + 2);
			row.setHeightInPoints(20);
			for (int j = 0; j < CERT_HEAD_TITLE.length; j++) {
				XSSFCell cell = row.createCell(j);
				if (j < CERT_HEAD_TITLE.length - 1) {
					if (j == 4 || j == 5) {
						// 设定时间格式
						CellStyle cellStyle = workbook.createCellStyle();
						cellStyle.cloneStyleFrom(cell.getCellStyle());
						DataFormat format= workbook.createDataFormat();
						cellStyle.setDataFormat(format.getFormat(DatePattern.NORM_DATE_PATTERN));
						if (Func.isNotBlank(itemMap.get(CERT_HEAD_TITLE[j]).toString())) {
							Date date = DateUtil.parse(Func.toStr(itemMap.get(CERT_HEAD_TITLE[j])), DatePattern.NORM_DATE_PATTERN);
							cell.setCellValue(date);
						}
						cell.setCellStyle(cellStyle);
					} else {
						cell.setCellValue(Func.toStr(itemMap.get(CERT_HEAD_TITLE[j])));
					}
				} else {
					// 设置字体为红色
					CellStyle cellStyle = workbook.createCellStyle();
					cellStyle.cloneStyleFrom(cell.getCellStyle());
					Font font = workbook.createFont();
					font.setColor(Font.COLOR_RED);
					cellStyle.setFont(font);
					cell.setCellStyle(cellStyle);
					cell.setCellValue(Func.toStr(itemMap.get("errMsg")));
				}
			}
		}

		String fileName = "证件导入错误数据_" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + ".xlsx";
		CommonUtil.exportStream(workbook, response, fileName);
	}

	@Override
	public void exportExcel(UserCertDTO userCert, HttpServletResponse response) throws IOException {
		List<UserCertVO> list = baseMapper.selectUserCertPage(null, userCert);
		if (list.isEmpty()) {
			throw new ServiceException("没有证件信息可以导出");
		}

		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList("姓名", "电话", "在职状态", "证件类型","作业项目", "证书编号", "有效期开始", "有效期结束", "证件状态", "创建日期", "最后操作日期");
			list.forEach(item -> {
				List<String> row = new ArrayList<>(8);
				row.add(Func.toStr(item.getRealName()));
				row.add(Func.toStr(item.getPhone()));
				row.add(item.getUserStatus() == 1 ? "在职" : "离职");
				row.add(Func.toStr(item.getCertName()));
				row.add(Func.toStr(item.getCertWorkName()));
				row.add(Func.toStr(item.getCertNum()));
				row.add(DateUtil.format(item.getStartTime(), DatePattern.NORM_DATE_PATTERN));
				row.add(DateUtil.format(item.getEndTime(), DatePattern.NORM_DATE_PATTERN));
				row.add(CertStatusEnum.getName(item.getStatus()));
				row.add(DateUtil.format(item.getCreateTime(), DatePattern.NORM_DATETIME_PATTERN));
				row.add(DateUtil.format(item.getUpdateTime(), DatePattern.NORM_DATETIME_PATTERN));
				rows.add(row);
			});
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.setColumnWidth(0, 30);
			excelWriter.setColumnWidth(1, 30);
			excelWriter.setColumnWidth(2, 30);
			excelWriter.setColumnWidth(3, 30);
			excelWriter.setColumnWidth(4, 30);
			excelWriter.setColumnWidth(5, 30);
			excelWriter.setColumnWidth(6, 30);
			excelWriter.setColumnWidth(7, 30);
			excelWriter.setColumnWidth(8, 30);
			excelWriter.setColumnWidth(9, 30);
			excelWriter.setColumnWidth(10, 30);

			// 单独设置头部
			excelWriter.writeHeadRow(title);
			String fileName = "证件信息_" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + ".xlsx";
			CommonUtil.setResponseHeader(response, fileName);
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			throw new ServiceException(e.getMessage());
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@Override
	public List<UserCertVO> selectAllList() {
		return baseMapper.selectAllList();
	}

	@Override
	public void sendMsgToAdministrator(String certContentTemplateFormat) {
		Client client = feiShuConfiguration.getClient(FeishuClientEnum.CERT);
        // 2025-02-28 发送范围变更：应用内范围全体 -> 应用范围内字典选取
		List<Dict> certOpenid = dictService.list(new LambdaQueryWrapper<Dict>()
			.eq(Dict::getCode, "cert_openid")
			.eq(Dict::getIsDeleted, 0)
			.ne(Dict::getParentId, 0)
		);
		if(Func.isEmpty(certOpenid)){
			return;
		}
        List<String> userIds = certOpenid.stream().map(Dict::getDictKey).collect(Collectors.toList());
//		try {
//			userIds = feiShuUtil.getOpenIdsByDefaultParam();
//		} catch (Exception e) {
//			throw new RuntimeException(e);
//		}
		if (CollUtil.isEmpty(userIds)) {
			throw new ServiceException("没有找到有效的管理员用户");
		}
		// 获取当前应用userId列表，发送消息
		if(Func.isNotEmpty(userIds)){
			userIds.forEach(u -> {
				CreateMessageReq createMessageReq = new CreateMessageReq();
				createMessageReq.setReceiveIdType("open_id");
				CreateMessageReqBody createMessageReqBody = new CreateMessageReqBody();
				createMessageReqBody.setMsgType("interactive");
				createMessageReqBody.setReceiveId(u);
				createMessageReqBody.setContent(certContentTemplateFormat);
				createMessageReq.setCreateMessageReqBody(createMessageReqBody);
				CreateMessageResp createMessageResp = null;
				try {
					createMessageResp = client.im().v1().message().create(createMessageReq);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			});
		}
	}
}
